<template>
  <div
    class="scroll-container"
    ref="scrollContainer"
    @scroll.passive="handleScroll"
  >
    <div :style="blankFillStyle">
      <!-- 根据待显示新闻列表数组，显示新闻列表 -->
      <div v-for="item in showDataList" :key="item.title">
        <slot :item="item"></slot>
      </div>
      <!-- 请求状态下 显示对应 msg 提示信息 -->
      <div v-if="isShowMsg" class="msg">
        <h2>{{ msg }}</h2>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "VirtualScroll",
  props: {
    // 提示信息
    msg: {
      type: String,
      default: "小二正在努力，请耐心等待...",
    },
    //单条新闻的高度
    onHeight: {
      type: Number,
      default: 100,
    },
    //数据来源 URL
    requestUrl: {
      type: String,
      default: "http://192.168.43.168:4000/data?num=",
    },
    //第一次请求数据的条数
    requestNum: {
      type: Number,
      default: 20,
    },
    //下拉触底时请求数据的条数
    moreRequestNum: {
      type: Number,
      default: 20,
    },
  },
  data() {
    return {
      // 用来保存 所有列表元素
      allDataList: [],
      // 数据请求状态判断
      isRequestStatus: true,
      //是否显示提示信息
      isShowMsg: true,
      //容器的最大容积
      containerMaxSize: 0,
      //滚动容器 可视区 第一个元素的索引
      startIndex: 0,

      //是否设置了请求帧标识
      isSetRequestAnimationFrame: false,

      //处理兼容性问题
      requestAnimationFrame:
        window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.msRequestAnimationFrame,

      //滚动距离
      currentScrollTop: 0,
    };
  },
  async created() {
    //请求新闻列表数据
    let newsList = await this.getNewsList(this.requestNum);
    if (!newsList) return;
    this.allDataList = newsList;
  },
  mounted() {
    this.getContainerMaxSize();

    //浏览器窗口大小或者设备纵横向 变化时，重新计算 容器的最大容积
    window.onresize = this.getContainerMaxSize;
    window.onorientationchange = this.getContainerMaxSize;
  },
  activated() {
    this.$nextTick(() => {
      //滚动到之间的位置
      this.$refs.scrollContainer.scrollTop = this.currentScrollTop;
    });
  },
  computed: {
    //滚动容器 可视区 最后一个元素的索引
    endIndex() {
      let endIndex = this.startIndex + this.containerMaxSize * 2;
      if (!this.allDataList[endIndex - 1]) {
        endIndex = this.allDataList.length;
      }
      return endIndex;
    },
    //定义一个 待显示的元素列表数组
    showDataList() {
      return this.allDataList.slice(this.getStartIndex(), this.endIndex);
    },
    //计算上下空白的填充高度
    blankFillStyle() {
      return {
        paddingTop: this.getStartIndex() * this.onHeight + "px",
        paddingBottom:
          this.onHeight * (this.allDataList.length - this.endIndex) + "px",
      };
    },
  },
  methods: {
    // 发送请求获取新的请求模拟数据，这个是跨域请求的网络mock数据
    getNewsList(num) {
      this.isRequestStatus = true;
      this.isShowMsg = true;
      this.msg = "小二正在努力，请耐心等待...";

      return (
        this.$axios
          //不要用localhost，否则移动端无法访问。
          //192.168.43.168是电脑局域网ipv4地址,查看方式：cmd命令 > ipconfig
          .get(this.requestUrl + num)
          .then((res) => {
            this.isRequestStatus = false;
            return res.data.list;
          })
          .catch(() => {
            this.msg = "亲，网络请求出错啦！赶快检查吧...";
            return false;
          })
      );
    },

    //计算容器的最大容积
    getContainerMaxSize() {
      this.containerMaxSize =
        ~~(this.$refs.scrollContainer.offsetHeight / this.onHeight) + 2;
    },

    //滚动事件
    handleScroll() {
      //滚动事件 节流优化 之 请求动画帧
      if (!this.isSetRequestAnimationFrame) {
        this.isSetRequestAnimationFrame = true;

        requestAnimationFrame(() => {
          this.updateData();
          this.isSetRequestAnimationFrame = false;
        });
      }

      //保存当前滚动距离
      this.currentScrollTop = this.$refs.scrollContainer.scrollTop;
    },

    //滚动事件 真正要执行的任务
    async updateData() {
      //垂直向上滚动的距离
      this.startIndex = ~~(
        this.$refs.scrollContainer.scrollTop / this.onHeight
      );

      //判断下拉是否触底
      if (
        this.startIndex + this.containerMaxSize > this.allDataList.length - 1 &&
        !this.isRequestStatus
      ) {
        //重新请求10条数据
        let newlist = await this.getNewsList(this.moreRequestNum);
        if (!newlist) return;
        this.allDataList.push(...newlist);
        this.isShowMsg = false;
      }
    },

    //计算 截取数据的缓冲区的 开始索引
    getStartIndex() {
      let startIndex = 0;
      if (this.startIndex < this.containerMaxSize) {
        startIndex = 0;
      } else {
        startIndex = this.startIndex - this.containerMaxSize;
      }
      return startIndex;
    },
  },
};
</script>

<style lang="scss" scoped>
.scroll-container {
  width: 100%;
  height: 100%;
  overflow-y: auto; //非常重要

  .msg h2 {
    font-size: 18px;
    text-align: center;
    color: #666;
    padding: 58px 0;
  }
}
</style>